package io.chenglicun.common.trans.service;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import io.chenglicun.common.trans.core.TransClassMeta;
import io.chenglicun.common.trans.core.TransField;
import io.chenglicun.common.trans.core.TransModel;
import io.chenglicun.common.trans.service.impl.TransRepository;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

@Service
public class TransService implements SmartInitializingSingleton {

    private final static ExecutorService EXECUTORS = Executors.newCachedThreadPool(ThreadUtil.newNamedThreadFactory("trans-thread-", false));

    /**
     * key type  val是对应type的service
     */
    private Map<Class<? extends TransRepository>, TransRepository> transRepositoryMap;

    /**
     * 如果objList 不为null就走 transMore 否则就走transOne
     *
     * @param obj 需要被翻译的对象
     */
    public void trans(Object obj, TransClassMeta info) {
        transRepositoryMap.forEach((transClass, transRepository) -> {
            List<TransField> transFields = info.getTransField(transClass);
            if (transFields.isEmpty()) {
                return;
            }
            Map<String, List<TransModel>> toTransMap = getToTransMap(obj, transFields);
            if (CollectionUtil.isNotEmpty(toTransMap)) {
                doTrans_0(transRepository, toTransMap);
            }
        });
        transRepositoryMap.forEach((transClass, transRepository) -> {
            List<TransField> transFields = info.getDependTransField(transClass);
            if (transFields.isEmpty()) {
                return;
            }
            Map<String, List<TransModel>> toTransMap = getToTransMap(obj, transFields);
            if (CollectionUtil.isNotEmpty(toTransMap)) {
                doTrans_0(transRepository, toTransMap);
            }
        });
    }

    private Map<String, List<TransModel>> getToTransMap(Object obj, List<TransField> toTransList) {
        Map<String, List<TransModel>> toTransMap = null;
        if (obj instanceof Collection<?>) {
            Collection<?> objList = (Collection<?>) obj;
            if (CollectionUtil.isNotEmpty(objList)) {
                toTransMap = toTransList.stream()
                        .flatMap(x -> objList.stream().map(o -> new TransModel(o, x)))
                        .filter(x -> StrUtil.isNotBlank(x.getTransVal()))
                        .filter(x -> Objects.isNull(ReflectUtil.getFieldValue(x.getObj(), x.getTransField().getField())))
                        .collect(Collectors.groupingBy(x -> x.getTransField().getTrans()));
            }
        } else {
            toTransMap = toTransList.stream()
                    .map(x -> new TransModel(obj, x))
                    .filter(x -> StrUtil.isNotBlank(x.getTransVal()))
                    .filter(x -> Objects.isNull(ReflectUtil.getFieldValue(x.getObj(), x.getTransField().getField())))
                    .collect(Collectors.groupingBy(x -> x.getTransField().getTrans()));
        }
        return toTransMap;
    }

    private void doTrans_0(TransRepository transRepository, Map<String, List<TransModel>> toTransMap) {
        //分组查询
        if (toTransMap.size() > 1) {
            //说明有多个实体，异步查询
            CompletableFuture<?>[] futures = toTransMap.values()
                    .stream()
                    .map(transModels -> CompletableFuture.runAsync(() -> doTrans(transRepository, transModels), EXECUTORS))
                    .toArray(CompletableFuture[]::new);
            CompletableFuture.allOf(futures).join();

        } else {
            //直接查询
            List<TransModel> transModels = toTransMap.values().iterator().next();
            doTrans(transRepository, transModels);
        }
    }

    private void doTrans(TransRepository transRepository, List<TransModel> transModels) {
        TransModel transModel = transModels.get(0);
        List<String> transValues = transModels.stream().flatMap(x -> Arrays.stream(x.getTransVal().split(","))).distinct().collect(Collectors.toList());
        Map<String, Object> valueMap = transRepository.getTransValueMap(transModel.getTransField(), transValues);
        if (!valueMap.isEmpty()) {
            Map<String, List<TransModel>> transMap = transModels.stream().collect(Collectors.groupingBy(TransModel::getTransVal));
            transMap.forEach((transVal, list) -> {
                String[] tsvs = transVal.split(",");
                for (String tsv : tsvs) {
                    Object value = valueMap.get(tsv);
                    if (value != null) {
                        Map<?, ?> map = convertToMap(value);
                        list.forEach(m -> m.setValue(map.get(m.getTransField().getKey())));
                    }
                }
            });
            return;
        }
        List<String> keys = transModels.stream().map(x -> x.getTransField().getKey()).distinct().collect(Collectors.toList());
        Map<String, Object> keysMap = transRepository.getKeysMap(transModel.getTransField(), keys);
        if (!keysMap.isEmpty()) {
            Map<String, List<TransModel>> transMap = transModels.stream().collect(Collectors.groupingBy(x -> x.getTransField().getKey()));
            transMap.forEach((key, list) -> {
                Object value = keysMap.get(key);
                if (value != null) {
                    Map<?, ?> map = convertToMap(value);
                    list.forEach(m -> {
                        String[] tsvs = m.getTransVal().split(",");
                        for (String tsv : tsvs) {
                            m.setValue(map.get(tsv));
                        }
                    });
                }
            });
        }
    }

    private Map<?, ?> convertToMap(Object bean) {
        if (bean instanceof Map<?, ?>) {
            return (Map<?, ?>) bean;
        }
        return BeanMap.create(bean);
    }

    @Override
    public void afterSingletonsInstantiated() {
        transRepositoryMap = SpringUtil.getApplicationContext().getBeansOfType(TransRepository.class)
                .values()
                .stream()
                .collect(Collectors.toMap(TransRepository::getClass, x -> x));
    }

}
